package com.ruoyi.system.service.sso;

import com.jayway.jsonpath.JsonPath;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.system.domain.IndsvrSsoApp;
import com.ruoyi.system.domain.IndsvrSsoAppAuth;
import com.ruoyi.system.domain.IndsvrSsoAppUserMap;
import com.ruoyi.system.service.ISysMenuService;
import com.ruoyi.system.service.ISysUserService;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Data;
import okhttp3.Cookie;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.token.TokenService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class PassiveFnos implements SimulationLogin , SimulationLogin.Passive {

    @Value("${token.secret}")
    private String secret;

    @Autowired
    private RedisCache redisCache;

    // 令牌有效期（默认30分钟）
    @Value("${token.expireTime}")
    private int expireTime;

    @Autowired
    private ISysUserService userService;

    @Autowired
    private ISysMenuService menuService;

    @Override
    public String getCodeUrl(IndsvrSsoApp app) {

        IndsvrSsoAppAuth indsvrSsoAppAuth = null;
        for (IndsvrSsoAppAuth indsvrSsoAppAuthItem : app.getIndsvrSsoAppAuthList()) {
            if ("oauth2KV".equals(indsvrSsoAppAuthItem.getType())) {
                indsvrSsoAppAuth=indsvrSsoAppAuthItem;
            }
        }

        if (indsvrSsoAppAuth==null) {
            throw new RuntimeException("未配置oauth2KV类型的授权信息，key为clientId，valPath为clientSecret");
        }
        return app.getBaseUrl() + "/signin?client_id="+indsvrSsoAppAuth.getAuthKey()+"&redirect_uri="+app.getCallbackUrl()+"&state=";
    }

    @Override
    public Object run(IndsvrSsoApp app, IndsvrSsoAppUserMap user, HttpServletRequest request) throws Exception {

        if (user.getUserId()==null) {//第一次调用

            OkHttpUtils okHttpUtils = new OkHttpUtils();

            IndsvrSsoAppAuth indsvrSsoAppAuth = null;
            for (IndsvrSsoAppAuth indsvrSsoAppAuthItem : app.getIndsvrSsoAppAuthList()) {
                if ("oauth2KV".equals(indsvrSsoAppAuthItem.getType())) {
                    indsvrSsoAppAuth=indsvrSsoAppAuthItem;
                }
            }

            if (indsvrSsoAppAuth==null) {
                throw new RuntimeException("未配置oauth2KV类型的授权信息，key为clientId，valPath为clientSecret");
            }

            byte[] bytes = (indsvrSsoAppAuth.getAuthKey()+":"+indsvrSsoAppAuth.getAuthValPath()).getBytes(StandardCharsets.UTF_8);
            byte[] encode = Base64.getEncoder().encode(bytes);
            okHttpUtils.putHead("Authorization","basic "+ new String(encode));

            String jsonBody="{\n" +
                    "    \"grant_type\": \"authorization_code\",\n" +
                    "    \"code\": \""+user.getAuthCode()+"\",\n" +
                    "    \"redirect_uri\": \"http://127.0.0.1\"\n" +
                    "}";
            String s = okHttpUtils.postForJson(app.getBaseUrl() + "/oauthapi/token", jsonBody);
            String token = JsonPath.read(s, "$.data.access_token");
            okHttpUtils.putHead("Authorization",token);
            String s2 = okHttpUtils.postForJson(app.getBaseUrl() + "/oauthapi/userinfo", jsonBody);
            String username = JsonPath.read(s2, "$.data.username");

            return username;
        }else {//第二次调用


            String token = IdUtils.fastUUID();
            Map<String, Object> claims = new HashMap<>();
            claims.put(Constants.LOGIN_USER_KEY, token);
            claims.put(Constants.JWT_USERNAME, user.getUsername());


            String token1 = Jwts.builder()
                    .setClaims(claims)
                    .signWith(SignatureAlgorithm.HS512, secret).compact();


            SysUser userSys = userService.selectUserByUserName(user.getUsername());


            LoginUser loginUser = new LoginUser(userSys.getUserId(), userSys.getDeptId(), userSys, getMenuPermission(userSys));

            loginUser.setLoginTime(System.currentTimeMillis());
            loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * 60*1000);
            // 根据uuid将loginUser缓存
            String userKey = CacheConstants.LOGIN_TOKEN_KEY + token;
            redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);

            Auths auths = new Auths();
            auths.token=token1;
            return auths;
        }

    }

    /**
     * 获取菜单数据权限
     *
     * @param user 用户信息
     * @return 菜单权限信息
     */
    public Set<String> getMenuPermission(SysUser user)
    {
        Set<String> perms = new HashSet<String>();
        // 管理员拥有所有权限
        if (user.isAdmin())
        {
            perms.add("*:*:*");
        }
        else
        {
            List<SysRole> roles = user.getRoles();
            if (!CollectionUtils.isEmpty(roles))
            {
                // 多角色设置permissions属性，以便数据权限匹配权限
                for (SysRole role : roles)
                {
                    if (StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && !role.isAdmin())
                    {
                        Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());
                        role.setPermissions(rolePerms);
                        perms.addAll(rolePerms);
                    }
                }
            }
            else
            {
                perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId()));
            }
        }
        return perms;
    }



    @Data
    public static class Auths{
        String token;
    }
}
